home *** CD-ROM | disk | FTP | other *** search
/ Mac Mania 6 / MacMania 6.toast / / Tools&Utilities / EnterAct Stuff / Documentation / Debugging hAWK programs < prev    next >
Text File  |  1992-04-05  |  17KB  |  413 lines

  1. Debugging hAWK programs
  2. -------------------
  3.  
  4. Introduction
  5. It doesn't run
  6. It doesn't do what I wanted
  7. Common bugs
  8.  
  9. ---------
  10. Introduction
  11. ---------
  12. Errors can creep in at the specification, design, or coding stage of any
  13. program, in any language. Symptoms of a error can range from a vague
  14. uneasiness about the results to seemingly random crashes. In C, one of the
  15. most difficult tasks of debugging is to stabilize a bug so that it can be
  16. repeated consistently; fortunately, in hAWK this isn't a problem, since it
  17. doesn't allow writing to an arbitrary memory address. So for hAWK
  18. programs, your tasks are to find where the bug is, and fix it.
  19.  
  20. This is mainly a guide to finding where the bug is in your source code, with a
  21. brief list of common bugs. When it's not obvious where the bug is, your two
  22. best weapons are; insert "print" statements to give you some idea of what's
  23. going on, and selectively comment out lines of source to isolate the problem.
  24. But as you gain experience writing hAWK programs you'll naturally find
  25. yourself avoiding the common bugs, and catching the others with careful
  26. proofreading.
  27.  
  28. Develop and test in small pieces. Follow a plan. Don't get mad, get critical.
  29. Question everything, including this cheap advice.
  30.  
  31. ---------
  32. It doesn't run
  33. ---------
  34. If your program doesn't start running due to a syntax error, the
  35. "$tempStdErr" file will contain a message telling you the line number
  36. where the error was detected. Normally this line number will be exactly
  37. where the error is, or at most a couple of lines after the real mistake.
  38.  
  39. Most syntax errors will be easy to spot and fix—missing punctuation such
  40. as brackets or quotes for example. One oddball error that may be difficult to
  41. diagnose is the insidious missing "#", as in
  42.     #$Calculate: a four function calulator.
  43.     #Enter expressions using numbers and + - * /.
  44.     #If the expression is not properly formed, as in
  45.      2 + 3 * / 7.5
  46.      #then you'll get an error message.
  47.     #....
  48. —this would produce the error message
  49.     hAWK:  syntax error near line 4:
  50.      2 + 3 * / 7.5
  51.                    ^ parse error
  52. in $tempStdErr. However, be thankful if you GET a message complaining
  53. about an uncommented comment. Quite often, you'll get no message at
  54. all—hAWK will quietly execute the comment as though it were part of your
  55. program (more on this below - see the start of "Common bugs").
  56.  
  57. Using a hAWK key word or builtin function name as a variable name is also a
  58. popular error—watch out especially for "in" and "length".
  59.  
  60. A carriage return or semicolon is sometimes required in a hAWK statement
  61. to disambiguate the syntax. See the "Grouping and breaking lines" section in
  62. the "hAWK program structure" chapter of the hAWK User's Manual for the
  63. details (that's section I 2 in the popup marks menu for the manual).
  64.  
  65. If you really get stuck trying to diagnose a syntax error, try looking through
  66. the sample programs supplied with hAWK for similar constructions, as well
  67. as rereading the relevant manual sections. See also the "Common bugs" section
  68. below.
  69.  
  70. ------------------
  71. It doesn't do what I wanted
  72. ------------------
  73.     Proofreading helpps
  74. hAWK programs are so easy to write that there is a strong temptation to
  75. rush. The fix for most of these problems is to carefully read through your
  76. new code once before running it. However, we're only human....
  77.  
  78.     Print power
  79. The best way to diagnose a bug in a program is to get your program to talk to
  80. you about what it is doing. Your most powerful debugging aid is built in to
  81. hAWK, and goes by the name of "print". The first rule of hAWK debugging is,
  82. Print Out What's Really Going On. Many of the suggestions below deal with
  83. printing out diagnostics.
  84.  
  85.     Make a copy
  86. If you're debugging an amibitious program, make a copy of it and debug the
  87. copy. By spinning off one or more versions of your program,you'll be able to
  88. back up if you angrily delete a stupid chunk of code, only to realise later that
  89. it was inspired and perfectly correct.
  90.  
  91.     Track your changes
  92. In a typical debugging session you will be inserting new statements on a trial
  93. or temporary basis, and also deleting old statements. It can be difficult to
  94. back up, and easy to get lost in a knot of conditional trials, so the second rule
  95. of hAWK debugging is Mark Your Changes. To temporarily comment out a
  96. statement, place "##" in front of it, rather than a single "#". If a new
  97. statement may not be permanent, place "###" after it. Later, you can
  98. search for your changes by looking for "##" and "###". Any variation on
  99. this such as "#@" is perfectly fine; the goal is to be able to spot all of your
  100. changes at any time by using your "Search" command in your editor. 
  101.  
  102. You can even separate out connected changes by tagging the affected lines with
  103. "##1" for one group, "##2" for another (the tag goes at the beginning of a
  104. line for a delete, at the end of the line for a newly-added statement). But
  105. this is getting a bit complicated, so do it only as a last resort.
  106.  
  107.     Variable values
  108. To check the value of a variable (say x1), you might as well keep it simple:
  109.     print x1 ###
  110. will do the job. If you are checking many variables, something like
  111.     print "x1 =", x1, "at line 43" ###
  112. may be called for.
  113.  
  114.     Variable names
  115. There is no such thing as an undeclared variable in hAWK. This convenience
  116. can trip you up, however, if you accidentally misspell the name of a
  117. variable. There will be no syntax error; the misspelled name will just be
  118. treated as a different variable. If you suspect a spelling error is the culprit
  119. but can't spot it, run $WordFrequency using your problem program as the
  120. input. This will produce a list of all words in your program, making it easier
  121. to pick out wrong spellings.
  122.  
  123. Note to have $WordFrequency skip over comments, you can uncomment 
  124.     ##/^#/ {next} #skip lines containing hAWK comments
  125. just after the "BEGIN" block in it.
  126.  
  127.     Assertions
  128. Assertions are easily checked by adding an "assert" function:
  129.     function assert(expr, message, line)
  130.         {
  131.         if (!expr)
  132.             print "Assertion flunked:", message, "at", line
  133.         }
  134. with usage such as
  135.     assert(x1 <= 50, "x1 <= 50", 43) ###
  136. --note that this still prints properly if you leave out the line
  137. number as in assert(x1 <= 50, "x1 <= 50") - you just won't 
  138. get the line number where the problem occurred.
  139.  
  140.     Assertions the easy way
  141. For the truly lazy, you could put your assertions in using an
  142. abbreviated form, such as
  143.     a(x1 <= 50)...
  144.     a(max > 0 && max < 1000) etc
  145. and then run this little hAWK program, which is in your "hAWK
  146. programs" folder, on your problem program to fill out the
  147. assertions (note it's been debugged):
  148.  
  149. # $ExpandAssertions : see "Debugging hAWK programs"
  150. # (the tricky bits - treat quotes properly, and
  151. # avoid using sub(), since the replacement string might contain
  152. # a "&" which stands for "everything that was matched".)
  153. #
  154. # Pass it your problem program as the single input file:
  155. # overwrites the file, replacing         a(assertion)          with
  156. #         assert(assertion, "assertion", line number) ###
  157. FNR == 1 {outfile = FILENAME}
  158.  {    if (match($0, /[ \t]*a\((.+)\)/))
  159.         {
  160.         match($0, /\((.+)\)/) #find the argument proper
  161.         first = substr($0, RSTART+1, RLENGTH-2) #copy it
  162.         second = first
  163.         gsub(/"/, "\\\"", second) #escape quotes
  164.         match($0, /[ \t]*/) #match starting white space
  165.         starter = substr($0, RSTART, RLENGTH) #copy it
  166.         $0 = starter "assert(" first ", \"" second "\", " FNR ") ###"
  167.         ##sub(/a\((.+)\)/, "assert(" first ", \"" second "\", " FNR ") ###")
  168.         ##-deleted, doesn't work properly if "second" contains a "&"
  169.         }
  170.     out[++i] = $0
  171.  }
  172. END {    close(outfile)
  173.         for (j = 1; j <= i; ++j)
  174.             print out[j] > outfile
  175.     }
  176.  
  177.  
  178. This will expand your abbreviations into proper assertions:
  179.     assert(x1 <= 50, "x1 <= 50", ddd) ###...
  180.     assert(max > 0 && max < 1000, "max > 0 && max < 1000",ddd) ### etc
  181. where ddd is the line number in your program. 
  182.  
  183. Add the "assert" function above to your program too!
  184.  
  185.     Function flow
  186. Tracing function flow can be done by inserting print statements
  187. at the start and end of each function, eg:
  188.     function a_func(args...)
  189.         {
  190.         print "Entering:""a_func"
  191.         ...body of function
  192.         print "Leaving:""a_func"
  193.         return something
  194.         }
  195. though the "Leaving" statements require more care, as your function
  196. might have several "return" statements. Often, just the "Entering"
  197. print statements provide enough information for debugging.
  198.  
  199.     Function flow the easy way
  200. "Entering" print statements can be inserted with the following
  201. hAWK program (once again your original program will be
  202. overwritten, so use a copy):
  203.  
  204. #$EnteringFunction: ad debugging to a program with functions,
  205. # inserting print statements at beginning of each function.
  206. # Pass it your problem program as the single input file:
  207. # overwrites the file, so use a copy.
  208. FNR == 1 {outfile = FILENAME}
  209.     {
  210.     if (match($0, /^[ \t]*func/)) #start of function definition
  211.         {
  212.         name = $2
  213.         len = index(name, "(")
  214.         if (len+0 > 1)
  215.             name = substr(name,1,len-1)
  216.         out[++i] = $0
  217.         # Skip over opening left curly of function
  218.         if ($0 !~ /{/)
  219.             {
  220.             do
  221.                 {
  222.                 getline
  223.                 out[++i] = $0
  224.                 } while ($0 !~ /{/);
  225.             }
  226.         out[++i] = "print \"Entering: \"\"" name "\" ###"
  227.         }
  228.     else
  229.         out[++i] = $0
  230.     }
  231. END {    close(outfile)
  232.         for (j = 1; j <= i; ++j)
  233.             print out[j] > outfile
  234.     }
  235.  
  236. You'll find this program in your "hAWK programs" folder.
  237.  
  238.     Sending diagostics to stderr
  239. If your program writes to stdout and you'd rather redirect your
  240. output to a different file to make things easier to read, then instead
  241. of just a plain "print" for your debugging
  242.         print "Debugging or error message"
  243. you can use
  244.         print("Debugging or error message") > "stderr"
  245. This will send diagnostics to the file $tempStdErr. The parenthesized
  246. form of the print statement should be used to make it clear to the
  247. interpreter that ">" means "redirect", not "greater than".
  248.  
  249. For example, to print assertions to $tempStdErr you could use
  250.     function assert(expr, message, line)
  251.         {
  252.         if (!expr)
  253.             print ("Assertion flunked:", message, "at", line) > "stderr"
  254.         }
  255. And to send function flow messages to stderr, replace the appropriate
  256. line in $EnteringFunction with
  257.     out[++i] = "print( \"Entering: \"\"" name "\") > \"stderr\" ###"
  258.  
  259. The $tempStdErr file will not be opened for you automatically after a run,
  260. so remember to open it and take a look if you send messages there.
  261.  
  262.     hAWK isn't C
  263. hAWK declares variables for you, happily concatenates just about anything
  264. with anything, accepts functions with a variable number of arguments,
  265. doesn't mind if you use a name as a number, a string, AND an array
  266. all in the same program, and just loves to print the current record to
  267. stdout unless you say otherwise. To some extent this is "too much of a
  268. good thing", and it certainly takes getting used to. The "Common bugs"
  269. section below is mostly a list of things that hAWK does differently from
  270. C, and a quick browse through will help you avoid programming in the
  271. wrong language.
  272.  
  273. ----------
  274. Common bugs
  275. ----------
  276. Uncommented comment: if you just can't find the bug, reread your program
  277. and look for a line that should be a comment but isn't. Forgetting to put
  278. the "#" at the start of a comment doesn't always cause a syntax error;
  279. often hAWK will execute the text as though it were code without error,
  280. and if the text involves any variables you use in your program then
  281. odd things can happen. Typical symptoms are that lines are printed to
  282. stdout that you didn't expect, or you can't seem to set the value of a
  283. variable.
  284. Watch for things like
  285.     #Setting
  286.             x = -1
  287.     #will shut off all progress dialogs,
  288.     #for quiet running.
  289. --here, "x = -1" would be interpreted as a pattern; it would always
  290. evaluate to nonzero, so all lines of input would be printed to stdout
  291. (the default action if no action is given).
  292. or
  293.     x = -1;     x = 0 will enable dlogs
  294. --here, x would have the value "0",  as a result of concatenating "0"
  295. with the (presumably) unassigned variables "will", "enable", and "dlogs",
  296. overriding the previous "x = -1" assignment. Strange, but true.
  297.  
  298. Spelling error: the drawback of not having to declare variables in hAWK is
  299. that a spelling error can accidentally create a new variable. For example,
  300.     if (maxLines > 50)
  301.         maxlines = 50
  302. would never set "maxLines" - it would create a new variable "maxlines"
  303. and set it instead. If you suspect a spelling error is the culprit but can't
  304. spot it, run $WordFrequency using your problem program as the input.
  305. This will produce a list of all words in your program, making it easier
  306. to pick out wrong spellings.
  307.  
  308. Unintentional redirection: "getline" returns 0 at end of file, -1 if there
  309. is a problem reading the file, and 1 if all is OK. So what does
  310.     if(getline < 0)....
  311. do? It attempts to read from the file named "0", and usually doesn't succeed.
  312. If you want to check that getline is not returning -1, use
  313.     if ((getline) < 0)...
  314. Even better, use
  315.     if(getline <= 0)....
  316. or
  317.     while (getline > 0)....
  318. instead.
  319.  
  320. Endless getline loop: "getline" returns 0 only if it has successfully reached
  321. the end of a file. If there is a problem opening or reading a file, "getline"
  322. returns -1. So,
  323.     while (getline < theFIle)....
  324. will loop forever if it has trouble reading a file. Use
  325.     while (getline < theFile > 0)....
  326. instead.
  327.  
  328. Unassigned variable: sometimes you may wish to safeguard against forgetting
  329. to assign a value to a variable, or to give it a default value if no value was
  330. ever assigned. The tests for this are:
  331.         if:                                        then this test is true:
  332.         ---------------        -----------------------
  333.         x is unassigned                if (x == "" && x == 0)
  334.         x = anything                    if (x != 0 || x != "")
  335.  
  336. For example,
  337.     if (find == "" && find == 0)
  338.         print "Oops, forgot to set \"find\"."
  339. More commonly, you'll just be interested in whether a variable has
  340. a non-null value, for which the test
  341.     if (x == "")
  342. will do.
  343.  
  344. Comparing string with number: in a comparison such as
  345.     if (x == y) or if (x >= y) etc
  346. x and y are compared as strings unless both x and y are numbers.
  347. To force the comparison to be done with the numeric values of the
  348. variables, add 0 to each, as in
  349.     if (x+0 == y+0) or if (x+0 >= y+0).
  350. Conversely, to force the comparison to be done with the string values,
  351. concatenate at least one with the null string, as in
  352.     if (x "" == y) or if (x >= y "").
  353.  
  354. Global instead of local: if you forget to declare a local variable, or misspell
  355. the name of a local variable, your variable will be global rather than
  356. local. It will not be initialized to zero each time you call the function, and
  357. if it is used elsewhere as a global then changing its value in one place will
  358. affect the other place. $WordFrequency helps with misspellings (see above).
  359.  
  360. Function returns garbage: if you forget to return something from a function
  361. then the returned value of the function will be garbage. If your function
  362. should return something, check that it does so in all possible cases.
  363.  
  364. Patterns are not mutually exclusive: more of a design problem than a bug,
  365. the problem here is to execute one action if some complicated test is true,
  366. and do some other action otherwise. To do it, put all the complicated testing
  367. inside one action, so you can use an "if-else" construction. 
  368. All patterns in your program are executed for each new input line, unless
  369. you do one of:
  370.     "next", which retrieves the next input line and starts the pattern
  371.     matching over with your first pattern;
  372.     "getline", which retrieves the next input line to $0 (if no variable
  373.     is specified) but doesn't jump you back to the first pattern;
  374.     "exit" which skips to your END statements or exits immediately.
  375.         
  376. Exit doesn't exit: an "exit" inside an END block does truly and immediately
  377. exit. An exit anywhere else passes control to your END statements if there
  378. are any, so if you need to do an immediate exit in a program that contains
  379. an END block use something like
  380.     ...
  381.     if (should immediately exit)
  382.         {
  383.         exit_now = 1
  384.         exit
  385.         }
  386.     ...
  387.     END {
  388.         if (exit_now == 1)
  389.             exit
  390.         ...normal END actions
  391.         }
  392.  
  393. Regular expression matches too much: regular expressions try to match
  394. as much as possible - for example, /A.*B/ will match everything between
  395. the first "A" on the line and the last "B" on the line, even if there are 5 B's
  396. in between. To match from A to the first B, use /A[^B]*B/.
  397.  
  398. Matching C identifiers: "\w" in a regular expression is the same as
  399. "[A-Za-z0-9]" - note it doesn't include the underscore. To match a C
  400. name, use "[A-Za-z_][A-Za-z0-9_]*" or the equivalent
  401. "[A-Za-z_](\w|_)*". Using "[A-Za-z0-9_]+" will work provided
  402. you don't mind catching integers such as "0" or "317" as well as names.
  403.  
  404. Missing array index: consider the fragment
  405.     x[1]= 17
  406.     x = "huh?"
  407.     x[2] = "hi"
  408.     print x[1], x[2], x
  409. -will this run? You bet - a variable and an array can have the same name,
  410. with no interference between the two. The above fragment will print
  411.     17 hi huh?
  412. Needless to say, this "feature" should be avoided.
  413.